package com.badlogic.androidgames.mypirates;

import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;
import java.util.Random;

import android.graphics.Bitmap;
import android.graphics.Point;

import com.badlogic.androidgames.framework.Game;
import com.badlogic.androidgames.framework.Graphics;
import com.badlogic.androidgames.framework.Screen;
import com.badlogic.androidgames.framework.Input.TouchEvent;
import com.badlogic.androidgames.mypirates.Sprite.Animation;
import com.badlogic.androidgames.mypirates.Sprite.BasicEnemy;
import com.badlogic.androidgames.mypirates.Sprite.Cannon;
import com.badlogic.androidgames.mypirates.Sprite.Island;
import com.badlogic.androidgames.mypirates.Sprite.MySprite;
import com.badlogic.androidgames.mypirates.Sprite.Player;
import com.badlogic.androidgames.mypirates.Sprite.Rubble;
import com.badlogic.androidgames.mypirates.Sprite.Ship.MainSailDirections;
import com.badlogic.androidgames.mypirates.Sprite.Ship.ShipState;
import com.badlogic.androidgames.mypirates.Sprite.Whirl;

public class SinglePlayerGameScreen extends Screen{

	public enum GameState { Playing, Paused, PlayerLose, PlayerWin, AllDead};
	//Texture
	Bitmap backgroundTexture;
	Bitmap explodeTexture;
	
	//Object
	Hashtable<String, MySprite> gameObjects;
	
	Player player;
	ArrayList<BasicEnemy> enemies;
	ArrayList<Whirl> whirls;
	ArrayList<Island> islands;
	ArrayList<Rubble> rubbles;
	
	//Logic fields
	boolean isHumanTurn;
	GameState gameState;
	
	int playerLifeRemained = 2;
	
	//Miscs
	Random random;
	ArrayList<Animation> explodeAnimations;
	///////////////////////////////////////////////////////////
	
	public SinglePlayerGameScreen(Game game) {
		super(game);
		Graphics g = game.getGraphics();
		backgroundTexture = g.newPixmap("GameplayBackground.png", null);
		explodeTexture = g.newPixmap("cannonblast.png", null);
		
		random = new Random();
		isHumanTurn = true;
		gameState = GameState.Playing;
		
		enemies = new ArrayList<BasicEnemy>();
		whirls = new ArrayList<Whirl>();
		islands = new ArrayList<Island>();
		rubbles = new ArrayList<Rubble>();
		
		explodeAnimations = new ArrayList<Animation>();
		
		gameObjects = new Hashtable<String, MySprite>();
		
		generatePlayer(5, 5);
		generateEnemies();
		generateWhirls();
		generateIslands();
	}
	
	void generatePlayer(int x, int y){
		String key = x + "_" + y;
		player = new Player(game);
		gameObjects.put(key, player);
		player.Initialize();
		player.x = x;
		player.y = y;
		player.IsActive = true;
		
	}
	
	void generateEnemies(){
		BasicEnemy newEnemy;
		int numEnemies = random.nextInt(5) + 6;
		int x,y;
		int count =0;
		
		do{
			x = random.nextInt(11);
			y = random.nextInt(11);
			if (!(x >= 4 && x <= 6 && y >= 4 && y <= 6) && ((x > 0 && x < 10) || (y > 0 && y < 10)))
				if(!gameObjects.containsKey(x + "_" + y)){
					newEnemy = new BasicEnemy(game, x, y);
					newEnemy.Initialize();
					enemies.add(newEnemy);
					gameObjects.put(x + "_" + y, newEnemy);
					count++;
				}
		}while(count < numEnemies);
	}
	
	void generateWhirls(){
		Whirl newWhirl;
		int x, y;
		
		for(int i = 0; i < 2 ; i++){
			for(int j = 0 ; j < 2; j++){
				x = (i%2)*10;
				y = (j%2)*10;
				newWhirl = new Whirl(game, x, y);
				newWhirl.Initialize();
				whirls.add(newWhirl);
				gameObjects.put(x + "_" + y, newWhirl);
			}
		}
	}
	
	void generateIslands(){
		int numIslands = random.nextInt(5) + 6;
		int x,y;
		int count = 0;
		
		do{
			x = random.nextInt(10);
			y = random.nextInt(10);
			
			if(!(x >= 3 && x <= 7 &&  y>= 3 && y <= 7)){
				if(!gameObjects.containsKey(x + "_" + y)){
					Island newIsland = new Island(game, x, y);
					islands.add(newIsland);
					newIsland.Initialize();
					gameObjects.put(x + "_" + y, newIsland);
					count++;
				}
			}
		}while(count < numIslands);
	}
	
	
	
	@Override
	public void update(float gameTime) {
		List<TouchEvent> touchEvents = game.getInput().getTouchEvents();
		HandleInput(touchEvents);
		
		if(player.currentShipState == ShipState.Standby && isHumanTurn && !player.IsActive){
			isHumanTurn = false;
			for(BasicEnemy enemy : enemies){
				enemy.IsActive = true;
				Point nextMove = enemy.getNextMove(player.x, player.y);
				enemy.Move(nextMove.x, nextMove.y);
			}
			if(enemies.size() != 0){
				////Audio
			}
				
		}
		updateWhirl(gameTime);
		updateIsland(gameTime);
		updateRubble(gameTime);
		updateEnemy(gameTime);
		updatePlayer(gameTime);
	}

	void updateWhirl(float gameTime)
	{
		for(Whirl whirl : whirls){
			whirl.Update(gameTime);
		}
	}
	
	void updateIsland(float gameTime){
		for(Island island : islands){
			island.Update(gameTime);
		}
	}
	
	void updateRubble(float gameTime){
		for(Rubble r : rubbles){
			if(r.IsActive)
				r.Update(gameTime);
		}
	}
	
	boolean isDone = false;
	void updateEnemy(float gameTime){
		if(!isHumanTurn){
			boolean isAllEnemiesMoved = true;
			
			for(BasicEnemy enemy : enemies){
				if(!(enemy.currentShipState == ShipState.Standby) || enemy.IsActive)
					isAllEnemiesMoved = false;
				break;
			}
			
			if(isAllEnemiesMoved && !isDone){
				boolean isStillCanMove = false;
				
				do{
					isStillCanMove = false;
					for(BasicEnemy enemy : enemies){
						if(enemy.LastPosition.x != enemy.x || enemy.LastPosition.y != enemy.y){
							String key = enemy.x + "_" + enemy.y;
							
							if(!gameObjects.containsKey(key)){
								gameObjects.put(key, enemy);
								gameObjects.remove(enemy.LastPosition.x + "_" + enemy.LastPosition.y);
								enemy.LastPosition.x = enemy.x;
								enemy.LastPosition.y = enemy.y;
								isStillCanMove = true;
							}
							else{
								Class<? extends MySprite> type = gameObjects.get(key).getClass();
								if(type.equals(Island.class) || type.equals(Rubble.class) || type.equals(Player.class)){
									enemy.destroyed = true;
									if(type.equals(Player.class)){
										gameObjects.get(key).destroyed = true;
										ReplaceWithRubble(new Point(enemy.x, enemy.y));
									}
									Animation explodeAnimation = new Animation(explodeTexture, new Point(20, 28), new Point(15, 1), new Point(enemy.x, enemy.y));
									explodeAnimations.add(explodeAnimation);
									explodeAnimation.PlayFromFrameIndex(0);
									
									gameObjects.remove(enemy.LastPosition.x + "_" + enemy.LastPosition.y);
									enemy.LastPosition.x = enemy.x;
									enemy.LastPosition.y = enemy.y;
									isStillCanMove = true;
								}
							}
								
						}
					}
				}while(isStillCanMove);
				
				for(BasicEnemy enemy : enemies){
					if(enemy.LastPosition.x != enemy.x || enemy.LastPosition.y != enemy.y){
						String key = enemy.x + "_" + enemy.y;
						String lastPositionKey = enemy.LastPosition.x + "_" + enemy.LastPosition.y;
						
						if(gameObjects.containsKey(key)){
							Class<? extends MySprite> type = gameObjects.get(key).getClass();
							if(type.equals(BasicEnemy.class)){
								BasicEnemy e = (BasicEnemy)gameObjects.get(key);
								if(e.LastPosition.x == enemy.x || enemy.LastPosition.y == enemy.y){
									enemy.destroyed = true;
									e.destroyed = true;
									enemy.LastPosition.x = enemy.x;
									enemy.LastPosition.y = enemy.y;
									ReplaceWithRubble(new Point(enemy.x, enemy.y));
								}
							}
							gameObjects.remove(lastPositionKey);
							Animation explodeAnimation = new Animation(explodeTexture, new Point(20, 28), new Point(15, 1), new Point(enemy.x, enemy.y));
							explodeAnimations.add(explodeAnimation);
							explodeAnimation.PlayFromFrameIndex(0);
							
						}
						else{
							gameObjects.put(key, enemy);
							gameObjects.remove(enemy.LastPosition.x + "_" + enemy.LastPosition.y);
							enemy.LastPosition.x = enemy.x;
							enemy.LastPosition.y = enemy.y;
						}
					}
				}
				
				int count = 0;
				while(count < enemies.size()){
					if(enemies.get(count).destroyed){
						enemies.remove(count);
						///Audio
					}
					else
						count++;
				}
				
				isDone = true;
			}
			else if(isAllEnemiesMoved && isDone){
				boolean isExplodeAnimationsPlayed = true;
				for(Animation a : explodeAnimations){
					if(a.IsActive){
						a.update();
						isExplodeAnimationsPlayed = false;
					}
				}
				
				if(explodeAnimations != null && isExplodeAnimationsPlayed){
					isHumanTurn = true;
					player.IsActive = true;
					explodeAnimations.clear();
					isDone = false;
				}
			}
		}
		
		for(BasicEnemy enemy : enemies){
			enemy.Update(gameTime);
		}
	}
	
	boolean isCannon1Hit = false, isCannon2Hit = false;
	void updatePlayer(float gameTime){
		if(player.currentShipState == ShipState.Attacking){
			if(player.cannon.q1.size() == 0 && player.cannon.lowerExplodeAnimation != null){
				if(!player.cannon.lowerExplodeAnimation.IsPlayed())
					player.cannon.makeLowerExplode();
				else if(player.cannon.is1Exploded && !isCannon1Hit){
					String key = player.cannon.p1.x + "_" + player.cannon.p1.y;
					enemies.remove(gameObjects.get(key));
					ReplaceWithRubble(player.cannon.p1);
					isCannon1Hit = true;
				}
				else if(!player.cannon.is1Exploded && !isCannon1Hit){
					///Audio
				}
			}
			
			if(player.cannon.q2.size() == 0 && player.cannon.upperExplodeAnimation != null){
				if(!player.cannon.upperExplodeAnimation.IsPlayed())
					player.cannon.makeUpperExplode();
				else if(player.cannon.is2Exploded && !isCannon2Hit){
					String key = player.cannon.p2.x + "_" + player.cannon.p2.y;
					enemies.remove(gameObjects.get(key));
					ReplaceWithRubble(player.cannon.p2);
					isCannon2Hit = true;
				}
				else if(!player.cannon.is2Exploded && !isCannon2Hit){
					///Audio
				}
			}
			
			if(!player.cannon.isAnimating()){
				player.currentShipState = ShipState.Standby;
				player.IsActive = false;
				isCannon1Hit = isCannon2Hit = false;
			}
		}
		
		if(player.destroyed){
			if(playerLifeRemained > 0){
				Point newPosition = GenerateNewPositionPlayer();
				generatePlayer(newPosition.x, newPosition.y);
				playerLifeRemained--;
			}
			else{
				if(enemies.size() == 0)
					gameState = GameState.AllDead;
				else
					gameState = GameState.PlayerLose;
				player.currentShipState = ShipState.Ended;
			}
		}
		else if(enemies.size() == 0)
			gameState = GameState.PlayerWin;
		
		
		player.Update(gameTime);
	}
	
	@Override
	public void draw(float deltaTime) {
		Graphics g = game.getGraphics();
		DrawBackground(g);
		DrawWhirl(g);
		DrawIsland(g);
		DrawRubble(g);
		DrawEnemy(g);
		DrawPlayer(g);
		//Can xem lai doan nay can dua vao dau
		if(!isHumanTurn && isDone && explodeAnimations != null){
			for(Animation a : explodeAnimations){
				if(a.IsActive)
					a.draw(g);
			}
		}
		///not done yet
	}

	void DrawBackground(Graphics g){
		g.drawBitmap(backgroundTexture, 0, 0);
	}
	void DrawPlayer(Graphics g){
		player.Draw(g);
	}
	void DrawEnemy(Graphics g){
		for(BasicEnemy enemy : enemies){
			enemy.Draw(g);
		}
	}
	void DrawIsland(Graphics g){
		for(Island island : islands){
			island.Draw(g);
		}
	}
	void DrawWhirl(Graphics g){
		for(Whirl whirl : whirls){
			whirl.Draw(g);
		}
	}
	void DrawRubble(Graphics g){
		for(Rubble rub : rubbles){
			if(rub.IsActive)
				rub.Draw(g);
		}
	}
	
	
	public void HandleInput(List<TouchEvent> touchEvents){
		if(isHumanTurn && gameState == GameState.Playing){
			for(TouchEvent event : touchEvents){
				if(event.type == TouchEvent.TOUCH_UP && player.currentShipState == ShipState.Standby){
					Point pnt = MyGameHelper.ConvertToMapCordinate(event);
					
					if(pnt.x != -1 && pnt.y != -1){
						int retvalue = CanMove(pnt);
						
						if(retvalue != -1)
							player.IsActive = true;
						
						switch (retvalue) {
						case 1:
							gameObjects.remove(player.x + "_" + player.y);
							gameObjects.put(pnt.x + "_" + pnt.y, player);
							player.Move(pnt.x, pnt.y);
							break;
						case 2:
							Point newPoint = GenerateNewPositionPlayer();
							gameObjects.remove(player.x + "_" + player.y);
							gameObjects.put(newPoint.x + "_" + newPoint.y, player);
							player.MoveAndWhirl(pnt, newPoint);
							break;
						case 3:
							Point[] p = new Point[2];
							boolean[] isExploded = new boolean[2];
							CheckHit(p, isExploded);
							player.Fire(p, isExploded);
							break;
						default:
							
							break;
						}
					}
				}
			}
		}
	}
	
	private int CanMove(Point p) {
		if(p.x == player.x && p.y == player.y)
			return 3;
		
		boolean cod1 = (p.y == player.y) && Math.abs(p.x - player.x) == 1;
		boolean cod2 = (p.x == player.x) && Math.abs(p.y - player.y) == 1;
		
		String key = p.x + "_" + p.y;
		
		if(cod1 || cod2){
			if(gameObjects.containsKey(key)){
				if(gameObjects.get(key).getClass().equals(Whirl.class))
					return 2;
			}
			else
				return 1;
		}
		
		return -1;
	}
	
	private Point GenerateNewPositionPlayer() {
		int x, y;
		boolean flag = false;
		do{
			x = random.nextInt(11);
			y = random.nextInt(11);
			
			if(!gameObjects.containsKey(x + "_" + y))
				flag = true;
		}while(!flag);
		
		return new Point(x, y);
	}
	
	void CheckHit(Point[] p, boolean[] isExploded){
		boolean flag1 = false, flag2 = false;
		
		if(player.mainSailDirection == MainSailDirections.East || player.mainSailDirection == MainSailDirections.West){
			for(int i = 1; i <= Cannon.attackRange; i++){
				String key = player.x + "_" + (player.y - i);
				if(gameObjects.containsKey(key) && !flag1)
					if(gameObjects.get(key).getClass().equals(BasicEnemy.class)){
						flag1 = true;
						p[0] = new Point(player.x, player.y - i);
					}
				
				key = player.x + "_" + (player.y + i);
				if(gameObjects.containsKey(key) && !flag2)
					if(gameObjects.get(key).getClass().equals(BasicEnemy.class)){
						flag2 = true;
						p[1] = new Point(player.x, player.y + i);
					}
			}
			
			if(!flag1){
				if(player.y == 0)
					p[0] = null;
				else if(player.y - Cannon.attackRange <= 0)
					p[0] = new Point(player.x, 0);
				else
					p[0] = new Point(player.x, player.y - Cannon.attackRange);
			}
			if(!flag2){
				if(player.y == 10)
					p[1] = null;
				else if(player.y + Cannon.attackRange >= 10)
					p[1] = new Point(player.x, 10);
				else
					p[1] = new Point(player.x, player.y + Cannon.attackRange);
			}
		}
		else{
			for(int i = 1; i <= Cannon.attackRange; i++){
				String key = (player.x - i) + "_" + player.y;
				if(gameObjects.containsKey(key) && !flag1)
					if(gameObjects.get(key).getClass().equals(BasicEnemy.class)){
						flag1 = true;
						p[0] = new Point(player.x - i, player.y);
					}
				
				key = (player.x + i) + "_" + player.y;
				if(gameObjects.containsKey(key) && !flag2)
					if(gameObjects.get(key).getClass().equals(BasicEnemy.class)){
						flag2 = true;
						p[1] = new Point(player.x + i, player.y);
					}
			}
			
			if(!flag1){
				if(player.x == 0)
					p[0] = null;
				else if(player.x - Cannon.attackRange <= 0)
					p[0] = new Point(0, player.y);
				else
					p[0] = new Point(player.x - Cannon.attackRange, player.y);
			}
			if(!flag2){
				if(player.x == 10)
					p[1] = null;
				else if(player.x + Cannon.attackRange >= 10)
					p[1] = new Point(10, player.y);
				else
					p[1] = new Point(player.x + Cannon.attackRange, player.y);
			}
		}
		
		isExploded[0] = flag1;
		isExploded[1] = flag2;
	}
	
	void ReplaceWithRubble(Point point){
		String key = point.x + "_" + point.y;
		if(!gameObjects.containsKey(key) || !(gameObjects.get(key).getClass().equals(Rubble.class))){
			Rubble newRubble = new Rubble(game, point.x, point.y);
			newRubble.Initialize();
			newRubble.IsActive = true;
			rubbles.add(newRubble);
			gameObjects.put(key, newRubble);
		}
	}
	
	@Override
	public void pause() {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void resume() {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void dispose() {
		// TODO Auto-generated method stub
		
	}
	
}
